package splitframe.farm.serviceutil;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.InetAddress;
import java.net.MulticastSocket;
import java.util.logging.Logger;

/**
 * This service utility will either run as a daemon-like socket
 * listener, or will simply send a ping and receive a response.
 * The response received will only be a url that should be the
 * local network's available codebase server.
 * <br/>
 * There are two examples of use:
 * <br/>
 * <ol>
 * 	<li>As a server: MulticastServiceUtil multicastServiceUtil = new MulticastServiceUtil(true);</li>
 *  <li>As a way of obtaining the url to a codebase server that's broadcasting itself:
 *  	<ul><li>
 *  MulticastServiceUtil multicastServiceUtil = new MulticastServiceUtil(false);<br/>
	codebase = multicastServiceUtil.getCodebaseUrl();<br/>
	System.setProperty("java.rmi.server.codebase",codebase);<br/>
 *  	</li></ul>
 *  </li>
 * </ol>
 * @author Chris Coy
 *
 */
public class MulticastServiceUtil {
	public static final String DEFAULT_GROUP = "239.1.2.3";

	public static final int DEFAULT_PORT = 1234;

	public static final int DEFAULT_TTL = 1;

	InetAddress group;

	int port;

	int ttl;

	boolean serverMode = false;

	private Logger logger = Logger.getLogger(MulticastServiceUtil.class.toString());
	
	/**
	 * Creates a new instance. Set server mode to true if you plan
	 * to run this util as a daemon. Set it to false if you just want
	 * to get the codebase url.
	 * 
	 * If you set the server mode to true, this server will start up
	 * as a new thread just before this method exits.
	 * 
	 * @param serverMode
	 */
	public MulticastServiceUtil(boolean serverMode) {
		try {
			String groupStr = DEFAULT_GROUP;
			InetAddress group = InetAddress.getByName(groupStr);
			int port = DEFAULT_PORT;
			int ttl = DEFAULT_TTL;

			this.group = group;
			this.port = port;
			this.ttl = ttl;
			this.serverMode = serverMode;
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public void start() throws IOException {
		netStart();
	}

	public void stop() throws IOException {
		netStop();
	}

	MulticastSocket socket;

	BufferedReader in;

	OutputStreamWriter out;

	Thread listener;

	/**
	 * start up sockets, in and out readers, and start the listener thread
	 * @throws IOException
	 */
	void netStart() throws IOException {
		socket = new MulticastSocket(port);
		socket.setTimeToLive(ttl);
		socket.joinGroup(group);
		in = new BufferedReader(new InputStreamReader(new DatagramInputStream(
				socket), "UTF8"));
		out = new OutputStreamWriter(new DatagramOutputStream(socket, group,
				port), "UTF8");
		if (serverMode) {
			listener = new Thread() {
				public void run() {
					netReceive();
				}
			};
			listener.start();
		}
	}

	/** close up shop and leave town
	 * 
	 * @throws IOException
	 */
	void netStop() throws IOException {
		if(listener!=null) {
			listener.interrupt();
			listener = null;
		}
		socket.leaveGroup(group);
		socket.close();
	}

	/** 
	 * Returns the url of the available codebase server found via
	 * multicast. One MUST be running an instance of SplitFrameClassPathServer
	 * first!
	 * @return
	 * @throws IOException
	 */
	public String getCodebaseUrl() throws IOException {
		MulticastSocket _socket = new MulticastSocket(port);
		_socket.setTimeToLive(ttl);
		_socket.joinGroup(group);
		BufferedReader _in = new BufferedReader(new InputStreamReader(new DatagramInputStream(
				_socket), "UTF8"));
		OutputStreamWriter _out = new OutputStreamWriter(new DatagramOutputStream(_socket, group,
				port), "UTF8");

		_out.write("ping\n");
		_out.flush();
		String value = "ping";
		do {
			value = _in.readLine();
		} while(value.equals("ping"));
		
		return value;
	}

	/**
	 * send some data
	 * @param message
	 */
	void netSend(String message) {
		try {
			out.write(message + "\n");
			out.flush();
		} catch (IOException ex) {
			ex.printStackTrace();
		}
	}

	/**
	 * wait for data
	 *
	 */
	void netReceive() {
		try {
			Thread myself = Thread.currentThread();
			while (listener == myself) {
				String message = in.readLine();
				logger.fine("Q:" + message);
				if (message.equals("ping")) {

					String url = System.getProperty("java.rmi.server.codebase");
					logger.fine("A: " + url);

					if (serverMode && url != null && url.length() > 0)
						netSend(url);
				}
			}
		} catch (IOException ex) {
			System.out.println("- listener stopped");
			ex.printStackTrace();
		}
	}

	/** This is really a test method for those of you who don't
	 * use JUnit. No arguments are required. If a jar server is running,
	 * you should see the codebase url print out in the system console.
	 * @param args
	 * @throws IOException
	 */
	public static void main(String[] args) throws IOException {
		MulticastServiceUtil serviceUtil = new MulticastServiceUtil(false);
		String url = serviceUtil.getCodebaseUrl();
		System.out.println(url);
	}
}
